package com.seven.sevenchat.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seven.sevenchat.VO.TeamUserVO;
import com.seven.sevenchat.VO.UserVO;
import com.seven.sevenchat.common.ErrorCode;
import com.seven.sevenchat.common.TeamStatusEnum;
import com.seven.sevenchat.exception.BusinessException;
import com.seven.sevenchat.model.Team;
import com.seven.sevenchat.model.User;
import com.seven.sevenchat.model.UserTeam;
import com.seven.sevenchat.model.dto.TeamQuery;
import com.seven.sevenchat.model.request.TeamJoinRequest;
import com.seven.sevenchat.model.request.TeamQuitRequest;
import com.seven.sevenchat.model.request.TeamUpdateRequest;
import com.seven.sevenchat.controller.TeamController;
import com.seven.sevenchat.controller.UserController;
import com.seven.sevenchat.service.TeamService;
import com.seven.sevenchat.mapper.TeamMapper;
import com.seven.sevenchat.service.UserService;
import com.seven.sevenchat.service.UserTeamService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
* 针对表【team(队伍)】的数据库操作Service实现
*/
@Service
@Slf4j
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService{

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private UserService userService;

    @Resource
    private RedissonClient redissonClient;


    /**
     * 创建队伍
     * @param team
     * @param loginUser
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public long addTeam(Team team, User loginUser) {
        //请求参数是否为空？
        if (team == null){throw  new BusinessException(ErrorCode.NULL_ERROR);}
        //是否登录？
        if (loginUser == null) {throw new BusinessException(ErrorCode.NOT_LOGIN);}
        final long userId = loginUser.getId();
        //队伍人数大小？
        Integer maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxNum < 1 || maxNum >20){throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍人数不符合要求");}
        //队伍标题长度？
        String name = team.getName();
        if (StringUtils.isBlank(name) || name.length() > 20) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题不满足要求");}
        //队伍描述长度？
        String description = team.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");}
        //status是否加密？（默认为 0 不加密）
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if (statusEnum == null) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");}
        //加密=>有密码&密码长度？
        String password = team.getPassword();
        if (TeamStatusEnum.SECRET.equals(statusEnum)) {
            if (StringUtils.isBlank(password) || password.length() > 32) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码设置不正确");}
        }
        //超时时间？
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间 > 当前时间");}
        //用户最多创建 5 个队伍？
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long hasTeamNum = this.count(queryWrapper);
        if (hasTeamNum >= 5) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建 5 个队伍");}
        //队伍表 插入对应信息
        team.setId(null);
        team.setUserId(userId);
        boolean result = this.save(team);
        Long teamId = team.getId();
        if (!result || teamId == null) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");}
        //用户队伍关系表 插入对应信息
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        result = userTeamService.save(userTeam);
        if (!result) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");}
        return teamId;
    }
    /**
     *  搜索队伍
     * @param teamQuery
     * @return
     */
    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery, boolean isAdmin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();

        if (teamQuery != null) {

            // 先get所有查询条件/查询字段
            Long id            = teamQuery.getId();
            List<Long> idList  = teamQuery.getIdList();
            String searchText  = teamQuery.getSearchText();
            String name        = teamQuery.getName();
            String description = teamQuery.getDescription();
            Integer maxNum     = teamQuery.getMaxNum();
            Long userId        = teamQuery.getUserId();
            Integer status     = teamQuery.getStatus();

            // 接下来判断各个查询条件/查询字段是否存在，若存在则根据此条件/字段进行查询
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }
            if (CollectionUtils.isNotEmpty(idList)) {
                queryWrapper.in("id", idList);
            }
            if (StringUtils.isNotBlank(searchText)) {//可以通过某个关键词同时对名称和描述查询
                queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
            }
            if (StringUtils.isNotBlank(name)) {
                queryWrapper.like("name", name);
            }
            if (StringUtils.isNotBlank(description)) {
                queryWrapper.like("description", description);
            }
            if (maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }
            if (userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
            if (statusEnum == null) {statusEnum = TeamStatusEnum.PUBLIC;}//没有状态算public
            if (!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)) //只有管理员才能查看加密还有非公开的房间
                {throw new BusinessException(ErrorCode.NO_AUTH);}
            queryWrapper.eq("status", statusEnum.getValue());
        }
        // 过滤已过期的队伍  expireTime is null or expireTime > now()
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        // 设置teamList，如果是空就直接淘汰
        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        // 关联查询：查询队伍创建人的用户信息
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (Team team : teamList) {
            Long userId = team.getUserId();
            if (userId == null) {continue;}
            User user = userService.getById(userId);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            // 脱敏用户信息
            if (user != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                teamUserVO.setCreateUser(userVO);
            }
            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;
    }
    /**
     *  update队伍更新
     * @param team
     * @param loginUser
     * @return
     */
    @Override
    public boolean updateTeam(TeamUpdateRequest team, User loginUser) {
//如果用户传入的新值和老值一致，就不用 update 了
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //先获取id，team，状态
        Long id = team.getId();
        Team oldTeam = this.getById(id);
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(team.getStatus());

        //判断请求参数是否为空，查询队伍是否存在
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (oldTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }

        //只有管理员或者队伍的创建者可以修改
        if (oldTeam.getUserId() != loginUser.getId() && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }

        //如果队伍状态改为加密，必须要有密码
        if (statusEnum.equals(TeamStatusEnum.SECRET)) {
            if (StringUtils.isBlank(team.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密房间必须要设置密码");
            }
        }
        Team updateTeam = new Team();
        BeanUtils.copyProperties(team, updateTeam);
        return this.updateById(updateTeam);
    }


    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        if (teamJoinRequest == null){
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        QueryWrapper<UserTeam> userTeamQueryWrapper;
        //先get
        Long teamId = teamJoinRequest.getTeamId();
        Team team = this.getById(teamId);
        Date expireTime = team.getExpireTime();
        Integer status = team.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        String password = teamJoinRequest.getPassWord();
        long userId = loginUser.getId();// 该用户已加入的队伍数量
        //队伍必须存在
        if (teamId == null || teamId <= 0){
            throw  new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (team == null){
            throw  new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");
        }
        //只能加入未过期队伍
        if (expireTime != null && expireTime.before(new Date())){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍已过期");
        }
        //禁止加入私有队伍
        if (TeamStatusEnum.PRIVATE.equals(teamStatusEnum)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"禁止加入私有队伍");
        }
        //如果加入的队伍是加密的，必须密码匹配才可以
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)){
            if (StringUtils.isBlank(password) || !password.equals(team.getPassword())){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码错误");
            }
        }
        // 只有一个线程能获取到锁
        RLock lock = redissonClient.getLock("yupao:join_team");
        try {
            // 抢到锁并执行
            while (true) {
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    System.out.println("getLock: " + Thread.currentThread().getId());
                    //用户最多加入 5 个队伍
                    userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("userId", userId);
                    if (userTeamService.count(userTeamQueryWrapper) > 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多创建和加入5个队伍");
                    }
                    //不能重复加入已加入的队伍
                    userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("userId", userId);
                    userTeamQueryWrapper.eq("teamId", teamId);
                    if (userTeamService.count(userTeamQueryWrapper) > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户已加入该队伍");
                    }
                    //只能加入未满队伍
                    userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("teamId", teamId);
                    if (userTeamService.count(userTeamQueryWrapper) >= team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已满");
                    }
                    //加入人员，修改队伍信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(userId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }
        }catch (InterruptedException e) {
            log.error("doCacheRecommendUser error", e);
            return false;
        } finally {
            // 只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        if (teamQuitRequest == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long teamId = teamQuitRequest.getTeamId();
        Team team = this.getById(teamId);
        long userId = loginUser.getId();
        UserTeam queryUserTeam = new UserTeam();
        queryUserTeam.setUserId(userId);
        queryUserTeam.setTeamId(teamId);
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(queryUserTeam);
        long count = userTeamService.count(queryWrapper);
        long teamHasJoinNum = this.countTeamUserByTeamId(teamId);

        if (teamId == null || teamId <= 0){
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        if (team == null){
            throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");
        }
        if (count == 0 ){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"未加入队伍");
        }

        // 队伍只剩一人，解散
        if (teamHasJoinNum == 1) {
            this.removeById(teamId);
        } else {
            // 是队长
            if (team.getUserId() == userId) {
                //
                // 查询已加入队伍的所有用户和加入时间
                QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                userTeamQueryWrapper.eq("teamId", teamId);
                userTeamQueryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                // 更新当前队伍的队长——最早加入的用户
                UserTeam nextUserTeam = userTeamList.get(1);
                Long nextTeamLeaderId = nextUserTeam.getUserId();
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setUserId(nextTeamLeaderId);
                boolean result = this.updateById(updateTeam);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队伍队长失败");
                }
            }
        }
        // 移除关系
        return userTeamService.remove(queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteTeam(long id, User loginUser) {
        // 校验队伍是否存在
        Team team = getTeamById(id);
        long teamId = team.getId();
        log.info(String.valueOf(team.getUserId()));
        log.info(String.valueOf(loginUser.getId()));
        // 校验你是不是队伍的队长
        if (!Objects.equals(team.getUserId(), loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH, "你没有删除的权限");
        }
        // 移除所有加入队伍的关联信息
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", teamId);
        boolean result = userTeamService.remove(userTeamQueryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除队伍关联信息失败");
        }
        // 删除队伍
        return this.removeById(teamId);
    }

    /**
     * 根据 id 获取队伍信息
     *
     * @param teamId
     * @return
     */
    private Team getTeamById(Long teamId) {
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        return team;
    }

    /**
     * 根据队伍id获取队伍中成员数量
     * @param teamId
     * @return
     */
    private long countTeamUserByTeamId(long teamId) {
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", teamId);
        return userTeamService.count(userTeamQueryWrapper);
    }
}




